package com.jm3.riscar3.gui;
import java.awt.Dimension;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SpringLayout;

import com.jm3.riscar3.core.CoreManager;
import com.jm3.riscar3.core.Microcontroller;
import com.jm3.riscar3.exceptions.EndOfProgramException;
import com.jm3.riscar3.exceptions.IOPortAddressingException;
import com.jm3.riscar3.exceptions.OutOfBoundsMemoryLocationException;
import com.jm3.riscar3.helpers.BinaryTranslator;

public class GraficalSimulator implements ActionListener, Runnable{
	final int TEXTFIELDSIZE=10;
	final int REGISTER_COUNT=8;
	private JOptionPane jPane;
	private JTextArea textArea;
	private JScrollPane scrollPane;
	private JPanel mainpanel;
	private ArrayList<JButton> buttons;
	private ArrayList<TextField> registerContent;
	private TextField IR,PC,SR,A, IOPorts, hexDisplay;
	private ArrayList<JLabel> registers;
	private JLabel lIR,lPC,lSR,lA,ltextArea,lIOPorts, lhexDisplay;
	private SpringLayout layout;
	private JFileChooser fc;
	private JFrame frame;
	private JButton openButton,stepButton,runButton;//, updateButton;
	public static Microcontroller mcu=new Microcontroller();
	public static CoreManager coreManager=new CoreManager(mcu);
	public void init()throws IOException{
		frame=new JFrame("RISC AR3 Simulator");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		defineAndAddComponentsToFrame();
		mainpanel.setPreferredSize(new Dimension(500,500));
		frame.setResizable(false);
		frame.pack();
		frame.setVisible(true);
	}

	private void defineAndAddComponentsToFrame() {
		
		buttons=new ArrayList<JButton>();
		registers=new ArrayList<JLabel>();
		registerContent=new ArrayList<TextField>();
		
		layout = new SpringLayout();
		mainpanel=new JPanel(layout);
			//textarea & scrollpane & label
		textArea=new JTextArea(20,20);
		scrollPane=new JScrollPane(textArea);
		mainpanel.add(scrollPane);
		ltextArea=new JLabel("Memory");
		mainpanel.add(ltextArea);
		//buttons
		openButton=new JButton("Choose file...");
		buttons.add(openButton);
		
		runButton=new JButton("Run");
		runButton.setEnabled(false);
		buttons.add(runButton);
		stepButton=new JButton("Step");
		stepButton.setEnabled(false);
		buttons.add(stepButton);
		//updateButton=new JButton("Update");
		//buttons.add(updateButton);
			//add listeners to buttons, add buttons to mainpanel
		for (JButton comp:buttons)
		{
			comp.addActionListener(this);
			mainpanel.add(comp);
		}
		
		//registers
			//register labels
		ArrayList<JLabel> tmpLabel=new ArrayList<JLabel>();
		ArrayList<TextField> tmpTextfield = new ArrayList<TextField>();
		for(int i=0;i<REGISTER_COUNT;i++){
			tmpLabel.add(new JLabel("R"+i));
			tmpTextfield.add(new TextField(TEXTFIELDSIZE));
		}
		for(JLabel lab:tmpLabel){
			registers.add(lab);
		}
			tmpLabel=null;
		lPC=new JLabel("PC");
		registers.add(lPC);
		lSR=new JLabel("SR");
		registers.add(lSR);
		lA=new JLabel("A  ");
		registers.add(lA);
		lIR=new JLabel("IR ");
		registers.add(lIR);
		
		lhexDisplay=new JLabel("Hex Display");
		lIOPorts=new JLabel("Keyboard Input");
			//register textfields
		for(TextField txt:tmpTextfield){
			registerContent.add(txt);
		}
		
		PC=new TextField(TEXTFIELDSIZE);
		registerContent.add(PC);
		SR=new TextField(TEXTFIELDSIZE);
		registerContent.add(SR);
		A=new TextField(TEXTFIELDSIZE);
		registerContent.add(A);
		IR=new TextField(TEXTFIELDSIZE*2);
		registerContent.add(IR);
		
		hexDisplay=new TextField(TEXTFIELDSIZE/2);
		IOPorts=new TextField(TEXTFIELDSIZE/2);
			//labels to textfields-add label to panel-add field to panel
		for(int i=0;i<REGISTER_COUNT;i++){
			registers.get(i).setLabelFor(registerContent.get(i));
			mainpanel.add(registers.get(i));
			mainpanel.add(registerContent.get(i));
		}
		
		lPC.setLabelFor(PC);
		mainpanel.add(lPC);
		mainpanel.add(PC);

		lSR.setLabelFor(SR);
		mainpanel.add(lSR);
		mainpanel.add(SR);
		
		lA.setLabelFor(A);
		mainpanel.add(lA);
		mainpanel.add(A);
		
		lIR.setLabelFor(IR);
		mainpanel.add(lIR);
		mainpanel.add(IR);
		
		lhexDisplay.setLabelFor(hexDisplay);
		mainpanel.add(lhexDisplay);
		mainpanel.add(hexDisplay);

		lIOPorts.setLabelFor(IOPorts);
		mainpanel.add(lIOPorts);
		mainpanel.add(IOPorts);
		
		fc=new JFileChooser();
		manageLayout();
		frame.add(mainpanel);
		
		
		
	}
	public void manageLayout(){
		//openbutton
		layout.putConstraint(SpringLayout.WEST, openButton, 5, SpringLayout.WEST, mainpanel);
		layout.putConstraint(SpringLayout.NORTH, openButton, 10, SpringLayout.NORTH, mainpanel);
		//setbutton
		layout.putConstraint(SpringLayout.WEST, stepButton, 5, SpringLayout.EAST, openButton);
		layout.putConstraint(SpringLayout.NORTH, stepButton, 10, SpringLayout.NORTH, mainpanel);
		//runbutton
		layout.putConstraint(SpringLayout.WEST, runButton, 5, SpringLayout.EAST, stepButton);
		layout.putConstraint(SpringLayout.NORTH, runButton, 10, SpringLayout.NORTH, mainpanel);
		//updatebutton
		//layout.putConstraint(SpringLayout.WEST, updateButton, 5, SpringLayout.EAST, runButton);
		//layout.putConstraint(SpringLayout.NORTH, updateButton, 10, SpringLayout.NORTH, mainpanel);
		
		//registers
			//textfield layout
		layout.putConstraint(SpringLayout.NORTH, registers.get(0), 50, SpringLayout.NORTH, mainpanel);
		layout.putConstraint(SpringLayout.NORTH, registerContent.get(0), 50, SpringLayout.NORTH, mainpanel);
		layout.putConstraint(SpringLayout.WEST, registerContent.get(0), 5, SpringLayout.EAST, registers.get(0));
		for(int i=1;i<registers.size();i++){
			layout.putConstraint(SpringLayout.NORTH, registers.get(i), 22, SpringLayout.SOUTH, registers.get(i-1));
			layout.putConstraint(SpringLayout.NORTH, registerContent.get(i), 15, SpringLayout.SOUTH, registerContent.get(i-1));
			layout.putConstraint(SpringLayout.WEST, registerContent.get(i), 5, SpringLayout.EAST, registers.get(i));
		}
		layout.putConstraint(SpringLayout.NORTH, scrollPane, 50, SpringLayout.NORTH, mainpanel);
		layout.putConstraint(SpringLayout.EAST, scrollPane, -50, SpringLayout.EAST, mainpanel);
		layout.putConstraint(SpringLayout.SOUTH, ltextArea, 1, SpringLayout.NORTH, scrollPane);
		layout.putConstraint(SpringLayout.WEST, ltextArea, 1, SpringLayout.WEST, scrollPane);
		
		layout.putConstraint(SpringLayout.NORTH, lhexDisplay, 10, SpringLayout.SOUTH, scrollPane);
		layout.putConstraint(SpringLayout.WEST, lhexDisplay, 0, SpringLayout.WEST, scrollPane);
		layout.putConstraint(SpringLayout.NORTH, hexDisplay, 5, SpringLayout.SOUTH, lhexDisplay);
		layout.putConstraint(SpringLayout.WEST, hexDisplay, 0, SpringLayout.WEST, lhexDisplay);
		
		layout.putConstraint(SpringLayout.NORTH, lIOPorts, 10, SpringLayout.SOUTH, hexDisplay);
		layout.putConstraint(SpringLayout.WEST, lIOPorts, 0, SpringLayout.WEST, hexDisplay);
		layout.putConstraint(SpringLayout.NORTH, IOPorts, 5, SpringLayout.SOUTH, lIOPorts);
		layout.putConstraint(SpringLayout.WEST, IOPorts, 0, SpringLayout.WEST, lIOPorts);
	}
	public void runGUI() {
		for(int i=0;i<coreManager.getINSTRUCTION_LOCATIONS();i++){
			step();
		}
		update();
	}
	public void step(){
		try {
			coreManager.step();
		} catch (IOPortAddressingException e) {
			int memLoc=Integer.valueOf(e.getMessage());
			/*digitalKeyboard dk = new digitalKeyboard();
			dk.init(mcu, memLoc);
			BufferedReader get=new BufferedReader(new InputStreamReader(System.in));
			
			System.out.println("Input a character. If more than one character is written, the first one will be chosen.");
			
			try {
				input=get.readLine();
			key=(int)input.charAt(0);
			
			} catch (Exception e1) {
				e1.printStackTrace();
			}*/
			String input;
			int key;
			input=JOptionPane.showInputDialog(mainpanel, "Input a character. If you write more than one character, the first one will be chosen.");
			key=input.charAt(0);
			try {
				mcu.getMemory().writeToLocation(memLoc, key);
			} catch (OutOfBoundsMemoryLocationException e1) {
				e1.printStackTrace();
			}
			mcu.getCPU().getAcc().writeRegister(key);
		}catch(EndOfProgramException e){
			System.out.println("File Execution Complete. Load another file.");
			runButton.setEnabled(false);
			stepButton.setEnabled(false);
			
		}
		catch(Exception e){
			e.printStackTrace();
		}
		update();
	}
	public void read(File file) {
		
		coreManager.read(file);
		runButton.setEnabled(true);
		stepButton.setEnabled(true);
		update();
	}
	private void update(){
		try{
			//Update memory
			int memContent;
			String display="";
			String keyboard="";
			String memHex="";
			textArea.setText("");
			for(int i=0;i<mcu.getMemorySize();i++){
				memContent=mcu.getMemory().readLocation(i);
				
				memHex=Integer.toHexString(memContent);
				if(i==250||251==i){
					keyboard+=(char)memContent;
				}
				if(i>251&&i<=256){
					display+=(char)memContent;
				}
				if(i%2==0){
					if(i<16){
						textArea.append("0");
					}
					textArea.append(Integer.toHexString(i)+":\t");
				}
				if(memContent<10){
					textArea.append("0");
				}
				textArea.append(memHex);
				if(i%2!=0){
					textArea.append("\n");
				}
			}
			//end-update memory
			//Update number Registers
			for(int z=0;z<8;z++)
			registerContent.get(z).setText(BinaryTranslator.getBitStringFromInt(mcu.getCPU().getRegisterX(z).readRegister(),8));
			//end-Update number Registers
			
			//Update SFRegisters
			//pc
			PC.setText(BinaryTranslator.getBitStringFromInt(mcu.getCPU().getPC().readRegister(),8));
			//sr
			SR.setText(BinaryTranslator.getBitStringFromInt(mcu.getCPU().getSR().readRegister(),4));
			//A
			A.setText(BinaryTranslator.getBitStringFromInt(mcu.getCPU().getAcc().readRegister(),8));
			//ir
			IR.setText(BinaryTranslator.getBitStringFromInt(mcu.getCPU().getIR().readRegister(),16));
			//end-Update SFRegisters
			IOPorts.setText(keyboard);
			hexDisplay.setText(display);
			
			
		}catch(Exception e){System.out.println(e);}
		
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		if(e.getSource()==openButton){
			int returnVal = fc.showOpenDialog(mainpanel);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
                File file = fc.getSelectedFile();
                read(file);
            }
            
		}
		else if(e.getSource()==stepButton){
			step();
		}
		else if(e.getSource()==runButton){
			runGUI();
		}
		//else if(e.getSource()==updateButton){
		//	update();
		//}
	}

	@Override
	public void run() {
		try {
			init();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}
